package com.hotel.room.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hotel.common.config.RabbitKeyConfig;
import com.hotel.common.param.*;
import com.hotel.common.vo.R;

import com.hotel.core.alipay.AliPayUtil;
import com.hotel.room.mapper.*;
import com.hotel.room.service.HotelRoomOrderService;
import com.hotel.user.entity.HotelRoom;
import com.hotel.room.service.HotelRoomService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hotel.user.entity.HotelRoomOrder;
import com.hotel.user.entity.PayLog;

import com.hotel.user.entity.UserWallet;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 酒店客房信息表 服务实现类
 * </p>
 *
 * @author trc
 * @since 2024-06-17
 */
@Service
public class HotelRoomServiceImpl extends ServiceImpl<HotelRoomMapper, HotelRoom> implements HotelRoomService {

    @Autowired
    private HotelRoomMapper hotelRoomMapper;

    @Autowired
    private HotelRoomOrderService hotelRoomOrderService;

    @Autowired
    private HotelRoomOrderMapper hotelRoomOrderMapper;

    @Autowired
    private HotelRoomRateMapper hotelRoomRateMapper;

    @Autowired
    private PayLogMapper payLogMapper;

    @Autowired
    private UserWalletMapper userWalletMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 根据选项查询不同类型的房间
     *
     * @param listType
     * @return
     */
    @Override
    public R showRoomList(String listType) {
        List<HotelRoom> roomList = null;
        QueryWrapper<HotelRoom> roomQueryWrapper = new QueryWrapper<>();
        if (listType.equals("a")) {
            roomQueryWrapper.eq("type",1);
            roomList = hotelRoomMapper.selectList(roomQueryWrapper);
            // 处理房间状态,当该房间最新的预约订单已经过期的时候,就把房间状态变回空闲
            for (HotelRoom room : roomList) {
                Integer roomId = room.getId();
                List<HotelRoomOrder> orderList = hotelRoomOrderMapper.selectOrderByRoomId(roomId);
                if (orderList != null && !orderList.isEmpty()) {
                    HotelRoomOrder hotelRoomOrder = orderList.get(0);
                    if (hotelRoomOrder.getEdate().getTime() < System.currentTimeMillis() && room.getStatus() != 1) {
                        room.setStatus(1);
                        hotelRoomMapper.updateById(room);
                    }
                } else if (room.getStatus() != 1) {
                    room.setStatus(1);
                }
            }
        } else if (listType.equals("b")) {
            roomQueryWrapper.eq("type",2);
            roomList = hotelRoomMapper.selectList(roomQueryWrapper);
            // 处理房间状态,当该房间最新的预约订单已经过期的时候,就把房间状态变回空闲
            for (HotelRoom room : roomList) {
                Integer roomId = room.getId();
                List<HotelRoomOrder> orderList = hotelRoomOrderMapper.selectOrderByRoomId(roomId);
                if (orderList != null && !orderList.isEmpty()) {
                    HotelRoomOrder hotelRoomOrder = orderList.get(0);
                    if (hotelRoomOrder.getEdate().getTime() < System.currentTimeMillis() && room.getStatus() != 1) {
                        room.setStatus(1);
                        hotelRoomMapper.updateById(room);
                    }
                } else if (room.getStatus() != 1) {
                    room.setStatus(1);
                }
            }
        } else if (listType.equals("c")) {
            roomQueryWrapper.eq("type",3);
            roomList = hotelRoomMapper.selectList(roomQueryWrapper);
            // 处理房间状态,当该房间最新的预约订单已经过期的时候,就把房间状态变回空闲
            for (HotelRoom room : roomList) {
                Integer roomId = room.getId();
                List<HotelRoomOrder> orderList = hotelRoomOrderMapper.selectOrderByRoomId(roomId);
                if (orderList != null && !orderList.isEmpty()) {
                    HotelRoomOrder hotelRoomOrder = orderList.get(0);
                    if (hotelRoomOrder.getEdate().getTime() < System.currentTimeMillis() && room.getStatus() != 1) {
                        room.setStatus(1);
                        hotelRoomMapper.updateById(room);
                    }
                } else if (room.getStatus() != 1) {
                    room.setStatus(1);
                }
            }
        } else if (listType.equals("d")) {
            roomQueryWrapper.eq("type",4);
            roomList = hotelRoomMapper.selectList(roomQueryWrapper);
            // 处理房间状态,当该房间最新的预约订单已经过期的时候,就把房间状态变回空闲
            for (HotelRoom room : roomList) {
                Integer roomId = room.getId();
                List<HotelRoomOrder> orderList = hotelRoomOrderMapper.selectOrderByRoomId(roomId);
                if (orderList != null && !orderList.isEmpty()) {
                    HotelRoomOrder hotelRoomOrder = orderList.get(0);
                    if (hotelRoomOrder.getEdate().getTime() < System.currentTimeMillis() && room.getStatus() != 1) {
                        room.setStatus(1);
                        hotelRoomMapper.updateById(room);
                    }
                } else if (room.getStatus() != 1) {
                    room.setStatus(1);
                }
            }
        } else {
            return R.fail("列表获取失败");
        }
        return R.ok(roomList);
    }

    @Override
    public R showRoomRate() {
        List<Integer> roomIdList = hotelRoomMapper.selectAllRoomId();
        for (Integer roomId : roomIdList) {
            List<Double> rateList = hotelRoomRateMapper.selectRateByRoomId(roomId);
            double sum = rateList.stream().mapToDouble(Double::doubleValue).sum();
            if (!rateList.isEmpty()) {
                double avg = sum / (rateList.size() * 1.0);
                int num = hotelRoomMapper.updateRateById(avg, roomId);
                if (num <= 0) {
                    return R.fail("评分获取失败");
                }
            }
        }
        return R.ok("评分获取成功");
    }

    /**
     * 展示我的房间列表
     *
     * 1.根据id来查询自己预约房间时的订单
     * 2.创建roomList集合存储userRoomShow对象
     * 3.遍历循环该用户的订单表,处理每一个房间订单
     * 4.初始化对象userRoomShow
     * 5.初始化userRoomShow的月份和日期
     * 6.判断预约的开始和结束时间,与当前时间的关系
     *
     * @param id
     * @return
     */
    @Override
    public R showMyRoomList(Integer id, ShowMyRoom showMyRoom) {
        String listType = showMyRoom.getListType();
        // 1.根据id来查询自己预约房间时的订单
        List<HotelRoomOrder> orderList = hotelRoomOrderMapper.selectOrderByIdType(id);
        if (orderList == null || orderList.isEmpty()) {
            return R.fail("没有房间");
        }

        HotelRoom hotelRoom = null;

        // 2.创建roomList集合存储userRoomShow对象
        // 之后跟随响应返回到前端
        List<UserRoom> roomList = new ArrayList<>();

        if (listType.equals("a")) {
            // 3.遍历循环该用户的订单表,处理每一个房间订单
            for (HotelRoomOrder order : orderList) {
                // 根据roomId获取房间信息属性
                // 4.初始化对象userRoomShow
                // 其中的属性包括: 预约是否过期的状态,还剩几天到期/入住,房间信息
                UserRoom userRoom = new UserRoom();
                Integer roomId = order.getRoomId();
                hotelRoom = hotelRoomMapper.selectRoomById(roomId);
                if (hotelRoom == null) {
                    return R.fail("获取房间失败");
                }
                userRoom.setHotelRoom(hotelRoom);

                // 5.初始化userRoomShow的月份和日期
                Date sdate = order.getSdate();
                Date edate = order.getEdate();

                // 初始化开始月份和日期
                Instant sInstant = sdate.toInstant();
                LocalDate slocalDate = sInstant.atZone(ZoneId.systemDefault()).toLocalDate();
                userRoom.setStartMonth(slocalDate.getMonthValue());
                userRoom.setStartDay(slocalDate.getDayOfMonth() + 1);

                // 初始化结束月份和日期
                Instant eInstant = edate.toInstant();
                LocalDate elocalDate = eInstant.atZone(ZoneId.systemDefault()).toLocalDate();
                userRoom.setEndMonth(elocalDate.getMonthValue());
                userRoom.setEndDay(elocalDate.getDayOfMonth() + 1);

                // 6.判断预约的开始和结束时间,与当前时间的关系
                if (edate.getTime() >= System.currentTimeMillis() && sdate.getTime() <= System.currentTimeMillis()) {
                    // 订单已生效,还在有效期内
                    // 订单剩余天数
                    long betweenRestDay = DateUtil.between(new Date(), order.getEdate(), DateUnit.DAY);
                    userRoom.setRestDays((int) betweenRestDay + 2);
                    // 订单总天数
                    long betweenDays = DateUtil.between(order.getSdate(), order.getEdate(), DateUnit.DAY);
                    userRoom.setDays((int) betweenDays);
                    userRoom.setAvailableType(1);
                    roomList.add(userRoom);
                }
            }
            return R.ok(roomList);
        } else if (listType.equals("b")) {
            // 3.遍历循环该用户的订单表,处理每一个房间订单
            for (HotelRoomOrder order : orderList) {
                // 根据roomId获取房间信息属性
                // 4.初始化对象userRoomShow
                // 其中的属性包括: 预约是否过期的状态,还剩几天到期/入住,房间信息
                UserRoom userRoom = new UserRoom();
                Integer roomId = order.getRoomId();
                hotelRoom = hotelRoomMapper.selectRoomById(roomId);
                if (hotelRoom == null) {
                    return R.fail("获取房间失败");
                }
                userRoom.setHotelRoom(hotelRoom);

                // 5.初始化userRoomShow的月份和日期
                Date sdate = order.getSdate();
                Date edate = order.getEdate();

                // 初始化开始月份和日期
                Instant sInstant = sdate.toInstant();
                LocalDate slocalDate = sInstant.atZone(ZoneId.systemDefault()).toLocalDate();
                userRoom.setStartMonth(slocalDate.getMonthValue());
                userRoom.setStartDay(slocalDate.getDayOfMonth() + 1);

                // 初始化结束月份和日期
                Instant eInstant = edate.toInstant();
                LocalDate elocalDate = eInstant.atZone(ZoneId.systemDefault()).toLocalDate();
                userRoom.setEndMonth(elocalDate.getMonthValue());
                userRoom.setEndDay(elocalDate.getDayOfMonth() + 1);

                // 6.判断预约的开始和结束时间,与当前时间的关系
               if (sdate.getTime() > System.currentTimeMillis()) {
                    // 订单还未生效
                    // 订单还有多少天开始执行
                    long betweenRestDay = DateUtil.between(new Date(), order.getSdate(), DateUnit.DAY);
                    userRoom.setRestDays((int) betweenRestDay + 2);
                    // 订单总天数
                    long betweenDay = DateUtil.between(order.getSdate(), order.getEdate(), DateUnit.DAY);
                    userRoom.setDays((int) betweenDay);
                    userRoom.setAvailableType(2);
                    roomList.add(userRoom);
                }
            }
            return R.ok(roomList);
        } else if (listType.equals("c")) {
            // 3.遍历循环该用户的订单表,处理每一个房间订单
            for (HotelRoomOrder order : orderList) {
                // 根据roomId获取房间信息属性
                // 4.初始化对象userRoomShow
                // 其中的属性包括: 预约是否过期的状态,还剩几天到期/入住,房间信息
                UserRoom userRoom = new UserRoom();
                Integer roomId = order.getRoomId();
                hotelRoom = hotelRoomMapper.selectRoomById(roomId);
                if (hotelRoom == null) {
                    return R.fail("获取房间失败");
                }
                userRoom.setHotelRoom(hotelRoom);

                // 5.初始化userRoomShow的月份和日期
                Date sdate = order.getSdate();
                Date edate = order.getEdate();

                // 初始化开始月份和日期
                Instant sInstant = sdate.toInstant();
                LocalDate slocalDate = sInstant.atZone(ZoneId.systemDefault()).toLocalDate();
                userRoom.setStartMonth(slocalDate.getMonthValue());
                userRoom.setStartDay(slocalDate.getDayOfMonth() + 1);

                // 初始化结束月份和日期
                Instant eInstant = edate.toInstant();
                LocalDate elocalDate = eInstant.atZone(ZoneId.systemDefault()).toLocalDate();
                userRoom.setEndMonth(elocalDate.getMonthValue());
                userRoom.setEndDay(elocalDate.getDayOfMonth() + 1);

                // 6.判断预约的开始和结束时间,与当前时间的关系
                if (edate.getTime() < System.currentTimeMillis()) {
                    // 订单已过期
                    userRoom.setDays(0);
                    userRoom.setAvailableType(0);
                    roomList.add(userRoom);
                }
            }
            return R.ok(roomList);
        }

        return R.fail("获取失败");

    }

    /**
     * 通过账户余额续费房间
     *
     * 1.判断该账户是否有足够的余额
     * 2.创建一个续费订单
     * 3.创建payLog支付信息写入pay_log支付信息表中
     * 4.扣减用户余额
     * 5.增加积分
     * 6.改变订单状态和房间状态
     *
     * @param renewRoomAdd
     * @return
     */
    @Override
    public R renewRoomByWallet(RenewRoomAdd renewRoomAdd) {

        // 1.判断该账户是否有足够的余额
        QueryWrapper<UserWallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", renewRoomAdd.getUserId());
        UserWallet userWallet = userWalletMapper.selectOne(queryWrapper);

        if (userWallet == null || userWallet.getMoney() < renewRoomAdd.getPrice() * 100) {
            return R.fail("用户余额不足");
        }

        // 2.创建一个续费订单
        HotelRoomOrder hotelRoomOrder = hotelRoomOrderService.createRenewOrder(renewRoomAdd);
        if (hotelRoomOrder == null) {
            return R.fail("房间已被预订,无法支付");
        }

        // 3.创建payLog支付信息写入pay_log支付信息表中
        PayLog payLog = new PayLog();
        payLog.setUserId(renewRoomAdd.getUserId());
        payLog.setPayType(2);

        payLog.setMoney((int) (renewRoomAdd.getPrice() * 100));
        payLog.setTitle("用户使用账户余额续费房间");
        payLog.setNo(hotelRoomOrder.getRoomNo());
        // 未支付状态
        payLog.setStatus(1);
        payLog.setCtime(new Date());
        payLog.setUtime(new Date());

        int i = payLogMapper.insert(payLog);
        // 判断订单是否插入成功
        if (i <= 0) {
            return R.fail("没有订单");
        }

        // 4.扣减用户余额
        // 扣减余额
        userWallet.setMoney(userWallet.getMoney() - (int) (renewRoomAdd.getPrice() * 100));
        userWallet.setUtime(new Date());
        int num = userWalletMapper.updateById(userWallet);
        if (num <= 0) {
            return R.fail("余额扣减失败");
        }

        // 5.增加积分
        rabbitTemplate.convertAndSend("",
                RabbitKeyConfig.Q_USER_GROWTH,
                new MqMsg(IdUtil.getSnowflakeNextId(),
                        4,
                        new GrowthAdd(payLog.getUserId(), "续费房间", 500)));

        // 6.改变订单状态和房间状态
        // 改变两个订单信息的支付状态
        hotelRoomOrder.setStatus(2);
        hotelRoomOrderMapper.updateById(hotelRoomOrder);
        payLog.setStatus(2);
        payLogMapper.updateById(payLog);

        QueryWrapper<HotelRoom> roomQueryWrapper = new QueryWrapper<>();
        roomQueryWrapper.eq("id", renewRoomAdd.getRoomId());
        HotelRoom hotelRoom = hotelRoomMapper.selectOne(roomQueryWrapper);
        if (hotelRoom == null) {
            return R.fail("未获取房间信息");
        }

        // 改变对应房间状态
        hotelRoom.setStatus(2);
        hotelRoom.setUtime(new Date());
        hotelRoomMapper.updateById(hotelRoom);

        return R.ok("预约成功");
    }

    /**
     * 通过支付宝预约房间
     *
     * 1.创建一个续费订单
     * 2.创建payLog支付信息写入pay_log支付信息表中
     * 3.调用支付宝支付页面
     *
     * @param renewRoomAdd
     * @return
     */
    @Override
    public R renewRoomByAliPay(RenewRoomAdd renewRoomAdd) {

        // 1.创建一个续费订单
        HotelRoomOrder hotelRoomOrder = hotelRoomOrderService.createRenewOrder(renewRoomAdd);
        if (hotelRoomOrder == null) {
            return R.fail("房间已被预订,无法支付");
        }

        // 2.创建payLog支付信息写入pay_log支付信息表中
        PayLog payLog = new PayLog();
        payLog.setUserId(renewRoomAdd.getUserId());
        payLog.setPayType(2);

        payLog.setMoney((int) (renewRoomAdd.getPrice() * 100));
        payLog.setTitle("用户使用账户余额续费房间");
        payLog.setNo(hotelRoomOrder.getRoomNo());
        // 未支付状态
        payLog.setStatus(1);
        payLog.setCtime(new Date());
        payLog.setUtime(new Date());

        int i = payLogMapper.insert(payLog);
        // 判断订单是否插入成功
        if (i > 0) {
            // 3.调用支付宝支付页面
            String html = AliPayUtil.createPay(payLog.getNo(), (double) payLog.getMoney(), "续费房间");
            return R.ok(html);
        }

        return R.fail("支付失败");

    }


    /**
     * 通过账户余额预约房间
     *
     * 1.判断该账户是否有足够的余额
     * 2.创建一个预约订单
     * 3.创建payLog支付信息写入pay_log支付信息表中
     * 4. 扣减用户余额
     * 5.增加积分
     * 6.改变订单状态和房间状态
     *
     * @param roomOrderAdd
     * @return
     */
    @Override
    public R payRoomByWallet(RoomOrderAdd roomOrderAdd) {

        // 1.判断该账户是否有足够的余额
        QueryWrapper<UserWallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id",roomOrderAdd.getUserId());
        UserWallet userWallet = userWalletMapper.selectOne(queryWrapper);

        if (userWallet == null || userWallet.getMoney() < roomOrderAdd.getPrice() * 100) {
            return R.fail("用户余额不足");
        }

        // 2.创建一个预约订单
        HotelRoomOrder hotelRoomOrder = hotelRoomOrderService.createRoomOrder(roomOrderAdd);
        if (hotelRoomOrder == null) {
            return R.fail("房间已被预订,无法支付");
        }

        // 3.创建payLog支付信息写入pay_log支付信息表中
        PayLog payLog = new PayLog();
        payLog.setUserId(roomOrderAdd.getUserId());
        payLog.setPayType(2);
        payLog.setMoney((int) (roomOrderAdd.getPrice() * 100));

        payLog.setTitle("用户使用账户余额预订房间");
        payLog.setNo(hotelRoomOrder.getRoomNo());
        // 未支付状态
        payLog.setStatus(1);
        payLog.setCtime(new Date());
        payLog.setUtime(new Date());

        int i = payLogMapper.insert(payLog);
        // 判断订单是否插入成功
        if (i <= 0) {
            return R.fail("没有订单");
        }

        // 4.扣减用户余额
        // 扣减余额
        userWallet.setMoney(userWallet.getMoney() - (int) (roomOrderAdd.getPrice() * 100));
        userWallet.setUtime(new Date());
        int num = userWalletMapper.updateById(userWallet);
        if (num <= 0) {
            return R.fail("余额扣减失败");
        }

        // 5.增加积分
        rabbitTemplate.convertAndSend("",
                RabbitKeyConfig.Q_USER_GROWTH,
                new MqMsg(IdUtil.getSnowflakeNextId(),
                        4,
                        new GrowthAdd(payLog.getUserId(), "预约房间", 500)));

        // 6.改变订单状态和房间状态
        // 改变两个订单信息的支付状态
        hotelRoomOrder.setStatus(2);
        hotelRoomOrderMapper.updateById(hotelRoomOrder);
        payLog.setStatus(2);
        payLogMapper.updateById(payLog);

        QueryWrapper<HotelRoom> roomQueryWrapper = new QueryWrapper<>();
        roomQueryWrapper.eq("id",roomOrderAdd.getRoomId());
        HotelRoom hotelRoom = hotelRoomMapper.selectOne(roomQueryWrapper);
        if (hotelRoom == null) {
            return R.fail("未获取房间信息");
        }

        // 改变对应房间状态
        hotelRoom.setStatus(2);
        hotelRoom.setUtime(new Date());
        hotelRoomMapper.updateById(hotelRoom);

        return R.ok("预约成功");
    }


    /**
     * 通过支付宝预约房间
     *
     * 1.创建一个会员订单
     * 2.创建payLog支付信息写入pay_log支付信息表中
     * 3.调用支付宝支付页面
     *
     * @param roomOrderAdd
     * @return
     */
    @Override
    public R payRoomByAliPay(RoomOrderAdd roomOrderAdd) {

        // 1.创建一个预约房间订单
        HotelRoomOrder hotelRoomOrder = hotelRoomOrderService.createRoomOrder(roomOrderAdd);
        if (hotelRoomOrder == null) {
            return R.fail("房间已被预订,无法支付");
        }

        // 2.创建payLog支付信息写入pay_log支付信息表中
        PayLog payLog = new PayLog();
        payLog.setUserId(roomOrderAdd.getUserId());
        payLog.setPayType(2);

        payLog.setMoney((int) (roomOrderAdd.getPrice() * 100));
        payLog.setTitle("用户使用支付宝预订房间");
        payLog.setNo(hotelRoomOrder.getRoomNo());
        // 未支付状态
        payLog.setStatus(1);
        payLog.setCtime(new Date());
        payLog.setUtime(new Date());

        int num = payLogMapper.insert(payLog);
        if (num > 0) {
            // 3.调用支付宝支付页面
            String html = AliPayUtil.createPay(payLog.getNo(), (double) payLog.getMoney(), "预约房间");
            return R.ok(html);
        }

        return R.fail("支付失败");
    }

}
